﻿using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Controls;
using System.Windows.Media;

namespace DimensionsHelper.Client.Controls;

[DebuggerDisplay("({Row}, {Column})")]
internal class BorderGridCell : BorderGridLineRenderer
{
    private readonly WeakReference<BorderGrid> _grid;
    private readonly Pen?[] _handledBorders;
    private List<BorderGridRange>? _containingRangeStack;
    private BorderCellFlags _flags;

    internal BorderGridCell(WeakReference<BorderGrid> grid, int row, int col)
    {
        _grid = grid;
        Row = row;
        Column = col;
        _flags = BorderCellFlags.None;
        _handledBorders = new Pen?[4];
    }


    /// <summary>
    ///     所在的表格布局对象
    /// </summary>
    private BorderGrid Grid
    {
        get
        {
            if (_grid.TryGetTarget(out BorderGrid? grid))
            {
                return grid;
            }

            throw new InvalidOperationException();
        }
    }


    /// <summary>
    ///     所在的区间定义
    /// </summary>
    public BorderGridRange? Range { get; set; }


    /// <summary>
    ///     边框对象在Grid布局中的行号，0开始
    /// </summary>
    public int Row { get; }


    /// <summary>
    ///     边框对象在Grid布局中的列号，0开始
    /// </summary>
    public int Column { get; }


    /// <summary>
    ///     如果此单元格被隐藏，返回<see langword="true" />。
    ///     包括所在 RowDefinition 或 ColumnDefinition 的宽度小于 1 的情况。
    /// </summary>
    private bool IsHidden
    {
        get
        {
            if (GetFlags(BorderCellFlags.Hidden))
            {
                return true;
            }

            RowDefinition? rowDef = Grid.RowDefinitions[Row];
            ColumnDefinition? colDef = Grid.ColumnDefinitions[Column];

            return rowDef.Height.Value == 0
                   || rowDef.ActualHeight < 1
                   || colDef.Width.Value == 0
                   || colDef.ActualWidth < 1;
        }
    }


    [MemberNotNullWhen(true, nameof(Range))]
    private bool WithinRange => Range != null;


    public BorderGridCell? NextLeftVisibleCell => GetNextVisibleCell(LeftGetter);

    public BorderGridCell? NextTopVisibleCell => GetNextVisibleCell(TopGetter);

    private BorderGridCell? NextRightVisibleCell => GetNextVisibleCell(RightGetter);

    private BorderGridCell? NextBottomVisibleCell => GetNextVisibleCell(BottomGetter);

    private bool IsFirstVisibleRowCell => IsSpecialVisibleCell(static c => c.Left);

    private bool IsFirstVisibleColumnCell => IsSpecialVisibleCell(static c => c.Top);

    private bool IsLastVisibleRowCell => IsSpecialVisibleCell(static c => c.Right);

    private bool IsLastVisibleColumnCell => IsSpecialVisibleCell(static c => c.Bottom);


    /// <summary>
    ///     如果是左上单元格，返回<see langword="true" />
    /// </summary>
    public bool IsTopLeft => Top == null && Left == null;


    /// <summary>
    ///     上方单元格
    /// </summary>
    public BorderGridCell? Top { get; set; }


    /// <summary>
    ///     下方单元格
    /// </summary>
    public BorderGridCell? Bottom { get; set; }


    /// <summary>
    ///     左侧单元格
    /// </summary>
    public BorderGridCell? Left { get; set; }


    /// <summary>
    ///     右侧单元格
    /// </summary>
    public BorderGridCell? Right { get; set; }


    private void HandleBorder(BorderPosition position, Pen pen)
    {
        Debug.Assert(_handledBorders[(int)position] == null);
        _handledBorders[(int)position] = pen;
    }


    private void ClearHandledBorders()
    {
        for (int i = 0; i < _handledBorders.Length; i++)
        {
            _handledBorders[i] = null;
        }
    }


    private bool IsBorderHandled(BorderPosition position)
    {
        return _handledBorders[(int)position] != null;
    }


    private Pen? GetHandledBorder(BorderPosition position)
    {
        return _handledBorders[(int)position];
    }


    /// <summary>
    ///     追加 <see cref="BorderCellFlags" /> 标记
    /// </summary>
    public void AddFlags(BorderCellFlags flags)
    {
        _flags |= flags;
    }


    /// <summary>
    ///     如果此节点在指定区间上包含指定 <see cref="BorderCellFlags" />，返回
    ///     <see langword="true" />
    /// </summary>
    private bool GetFlags(BorderCellFlags flags)
    {
        return (_flags & flags) != 0;
    }

    /// <summary>
    ///     移除指定的 <see cref="BorderCellFlags" /> 标记
    /// </summary>
    public void RemoveFlags(BorderCellFlags flags)
    {
        _flags &= ~flags;
    }


    private List<BorderGridRange> GetContainingRanges()
    {
        if (_containingRangeStack != null)
        {
            return _containingRangeStack;
        }

        List<BorderGridRange> stack = new();

        BorderGridRange? range = Range;

        while (range != null)
        {
            stack.Add(range);

            BorderGridRange? child = range.FindChild(this);

            if (child == null)
            {
                break;
            }

            range = child;
        }

        return _containingRangeStack = stack;
    }


    private Pen? GetPen(BorderType borderType)
    {
        List<BorderGridRange> ranges = GetContainingRanges();

        for (int i = ranges.Count - 1; i >= 0; i--)
        {
            BorderGridRange range = ranges[i];

            if ((borderType == BorderType.Around && !range.AddAroundBorder) ||
                (borderType == BorderType.Horizontal && !range.AddHorizontalBorder) ||
                (borderType == BorderType.Vertical && !range.AddVerticalBorder))
            {
                continue;
            }

            Pen? pen = range.GetPen();

            if (pen != null)
            {
                return pen;
            }
        }

        return null;
    }


    public void UpdateBorder()
    {
        List<BorderGridLineInfo>? infos = new();
        GetBorderLineInfos(infos);
        DrawLine(infos);
    }


    private BorderGridCell? GetNextVisibleCell(Func<BorderGridCell, BorderGridCell?> nextGetter)
    {
        BorderGridCell? next = nextGetter(this);

        while (next != null)
        {
            if (!next.IsHidden)
            {
                return next;
            }

            next = nextGetter(next);
        }

        return null;
    }


    private static BorderGridCell? LeftGetter(BorderGridCell cell)
    {
        return cell.Left;
    }

    private static BorderGridCell? TopGetter(BorderGridCell cell)
    {
        return cell.Top;
    }

    private static BorderGridCell? RightGetter(BorderGridCell cell)
    {
        return cell.Right;
    }

    private static BorderGridCell? BottomGetter(BorderGridCell cell)
    {
        return cell.Bottom;
    }


    private bool IsSpecialVisibleCell(Func<BorderGridCell, BorderGridCell?> nextGetter)
    {
        bool visible = !IsHidden;

        if (visible)
        {
            BorderGridCell? cell = nextGetter(this);

            while (cell != null)
            {
                if (cell.Range != Range && !cell.IsHidden)
                {
                    break;
                }

                if (!cell.IsHidden)
                {
                    return false;
                }

                cell = nextGetter(cell);
            }
        }

        return visible;
    }


    private void GetLeftBorder(List<BorderGridLineInfo> lines, Pen defaultPen, RowDefinition row,
        ColumnDefinition col,
        bool addAround, bool addVertical)
    {
        double fix = 0;

        if (Bottom is { IsHidden: true })
        {
            fix = 1;
        }

        if (IsBorderHandled(BorderPosition.Left))
        {
            lines.Add(new BorderGridLineInfo(
                GetHandledBorder(BorderPosition.Left),
                col.Offset,
                row.Offset,
                col.Offset,
                row.Offset + row.ActualHeight + fix));

            return;
        }

        if (!GetFlags(BorderCellFlags.MergedColumnInner))
        {
            Pen finalPen = defaultPen;

            bool isLeftBound = GetFlags(BorderCellFlags.LeftBound)
                               || (Range != null && IsFirstVisibleRowCell);

            if (Range != null)
            {
                if (isLeftBound && addAround)
                {
                    finalPen = GetPen(BorderType.Around) ?? defaultPen;
                }
                else if (!isLeftBound && addVertical && !GetFlags(BorderCellFlags.MergedColumnInner))
                {
                    finalPen = GetPen(BorderType.Vertical) ?? defaultPen;
                }
            }

            lines.Add(new BorderGridLineInfo(
                finalPen,
                col.Offset,
                row.Offset,
                col.Offset,
                row.Offset + row.ActualHeight + fix));
        }
    }


    private void GetTopBorder(List<BorderGridLineInfo> lines, Pen defaultPen, RowDefinition row,
        ColumnDefinition col,
        bool addAround, bool addHorizontal)
    {
        double fix = 0;

        if (Right is { IsHidden: true })
        {
            fix = 1;
        }

        if (IsBorderHandled(BorderPosition.Top))
        {
            lines.Add(new BorderGridLineInfo(
                GetHandledBorder(BorderPosition.Top),
                col.Offset,
                row.Offset,
                col.Offset + col.ActualWidth + fix,
                row.Offset));

            return;
        }

        if (!GetFlags(BorderCellFlags.MergedLineInner))
        {
            Pen finalPen = defaultPen;

            bool isTopBound = GetFlags(BorderCellFlags.TopBound)
                              || (Range != null && IsFirstVisibleColumnCell);

            if (Range != null)
            {
                if (isTopBound && addAround)
                {
                    finalPen = GetPen(BorderType.Around) ?? defaultPen;
                }
                else if (!isTopBound && addHorizontal && !GetFlags(BorderCellFlags.MergedLineInner))
                {
                    finalPen = GetPen(BorderType.Horizontal) ?? defaultPen;
                }
            }

            lines.Add(new BorderGridLineInfo(
                finalPen,
                col.Offset,
                row.Offset,
                col.Offset + col.ActualWidth + fix,
                row.Offset));
        }
    }


    private void GetRightBorder(List<BorderGridLineInfo> lines, Pen defaultPen, RowDefinition row,
        ColumnDefinition col,
        bool addAround)
    {
        double fix = 0;

        if (Bottom is { IsHidden: true })
        {
            fix = 1;
        }

        if (IsBorderHandled(BorderPosition.Right))
        {
            lines.Add(new BorderGridLineInfo(
                GetHandledBorder(BorderPosition.Right),
                col.Offset + col.ActualWidth,
                row.Offset,
                col.Offset + col.ActualWidth,
                row.Offset + row.ActualHeight + fix));

            return;
        }

        Pen finalPen = defaultPen;

        bool isRightBound = GetFlags(BorderCellFlags.RightBound)
                            || (Range != null && IsLastVisibleRowCell);

        if (Range != null && isRightBound && addAround)
        {
            finalPen = GetPen(BorderType.Around) ?? defaultPen;
        }

        BorderGridCell? rightCell = NextRightVisibleCell;

        // 如果右侧单元格不是 null，需要检查其是否在区间内和所在区间是否添加外边框
        if (rightCell != null)
        {
            if (addAround && isRightBound && (!rightCell.WithinRange || !rightCell.Range.AddAroundBorder))
            {
                rightCell.HandleBorder(BorderPosition.Left, finalPen);
            }
        }
        // 对于最右侧单元格，默认添加外边框
        else
        {
            lines.Add(new BorderGridLineInfo(
                finalPen,
                col.Offset + col.ActualWidth,
                row.Offset,
                col.Offset + col.ActualWidth,
                row.Offset + row.ActualHeight + fix));
        }
    }


    private void GetBottomBorder(List<BorderGridLineInfo> lines, Pen defaultPen, RowDefinition row,
        ColumnDefinition col, bool addAround)
    {
        double fix = 0;

        if (Right is { IsHidden: true })
        {
            fix = 1;
        }

        if (IsBorderHandled(BorderPosition.Bottom))
        {
            lines.Add(new BorderGridLineInfo(
                GetHandledBorder(BorderPosition.Bottom),
                col.Offset,
                row.Offset + row.ActualHeight,
                col.Offset + col.ActualWidth + fix,
                row.Offset + row.ActualHeight));

            return;
        }

        Pen finalPen = defaultPen;

        bool isBottomBound = GetFlags(BorderCellFlags.BottomBound)
                             || (Range != null && IsLastVisibleColumnCell);

        if (Range != null && isBottomBound && addAround)
        {
            finalPen = GetPen(BorderType.Around) ?? defaultPen;
        }

        BorderGridCell? bottomCell = NextBottomVisibleCell;

        // 如果下方单元格不是 null，检查其是否在区间内和添加外边框
        if (bottomCell != null)
        {
            // 1. bottom 不在区间内
            // 2. bottom 的区间不添加外边框
            if (isBottomBound && addAround && (!bottomCell.WithinRange || !bottomCell.Range.AddAroundBorder))
            {
                bottomCell.HandleBorder(BorderPosition.Top, finalPen);
            }
        }
        // 最下方的单元格，默认添加下边框
        else
        {
            lines.Add(new BorderGridLineInfo(
                finalPen,
                col.Offset,
                row.Offset + row.ActualHeight,
                col.Offset + col.ActualWidth + fix,
                row.Offset + row.ActualHeight));
        }
    }


    private void GetBorderLineInfos(List<BorderGridLineInfo> lines)
    {
        if (IsHidden)
        {
            return;
        }

        // 如果在合并单元格的中间，不带有任何边框
        if (GetFlags(BorderCellFlags.MergedLineInner) && GetFlags(BorderCellFlags.MergedColumnInner))
        {
            return;
        }

        // 需要检查下、右两侧的单元格，如果后面的区间添加了外边框，边界边框由后续的区间绘制
        // 
        // +----------+
        // |  Range   |  <---
        // +----------+  <--- 此处的边界边框应当由相邻的区间内单元格
        // |  Cell    |       调用相邻单元格的 HandleBorder 方法来绘制。
        // +----------+       
        //

        RowDefinition? rowDef = Grid.RowDefinitions[Row];
        ColumnDefinition? colDef = Grid.ColumnDefinitions[Column];

        if (rowDef.ActualHeight < 1 || colDef.ActualWidth < 1)
        {
            return;
        }

        bool addAround = false;
        bool addHorizontal = false;
        bool addVertical = false;

        foreach (BorderGridRange range in GetContainingRanges())
        {
            if (addAround && addHorizontal && addVertical)
            {
                break;
            }

            if (!addAround && range.AddAroundBorder)
            {
                addAround = true;
            }

            if (!addHorizontal && range.AddHorizontalBorder)
            {
                addHorizontal = true;
            }

            if (!addVertical && range.AddVerticalBorder)
            {
                addVertical = true;
            }
        }

        // 当单元格的下侧或右侧单元格被隐藏时，会有一个边框宽度左右的错位，需要在画边框时补正

        Pen? defaultPen = new(Grid.BorderBrush, 1d);

        GetLeftBorder(lines, defaultPen, rowDef, colDef, addAround, addVertical);
        GetTopBorder(lines, defaultPen, rowDef, colDef, addAround, addHorizontal);
        GetRightBorder(lines, defaultPen, rowDef, colDef, addAround);
        GetBottomBorder(lines, defaultPen, rowDef, colDef, addAround);

        ClearHandledBorders();
    }


    private enum BorderPosition
    {
        Left = 0,
        Top = 1,
        Right = 2,
        Bottom = 3
    }


    private enum BorderType
    {
        Around,
        Horizontal,
        Vertical
    }
}


internal static class BorderGridPens
{
    internal static readonly DoubleCollection DotDashArray = [1, 1];
    internal static readonly DoubleCollection DashedDashArray = [2, 2];
    internal static readonly DoubleCollection DashDotDashArray = [2, 1, 1, 1];
    internal static readonly DoubleCollection DashDotDotDashArray = [3, 1, 1, 1, 1, 1];

    internal static Pen GetPen(Brush brush, double thickness)
    {
        Pen? pen = new(brush, thickness);
        pen.Freeze();
        return pen;
    }

    private static Pen GetDashStylePen(Brush brush, double thickness, DoubleCollection dashArray)
    {
        Pen? pen = new(brush, thickness)
        {
            DashStyle = new DashStyle(dashArray, 0),
            DashCap = PenLineCap.Flat
        };

        pen.Freeze();
        return pen;
    }


    internal static Pen GetDotPen(Brush brush, double thickness)
    {
        return GetDashStylePen(brush, thickness, DotDashArray);
    }


    internal static Pen GetDashedPen(Brush brush, double thickness)
    {
        return GetDashStylePen(brush, thickness, DashedDashArray);
    }


    internal static Pen GetDashDotPen(Brush brush, double thickness)
    {
        return GetDashStylePen(brush, thickness, DashDotDashArray);
    }


    internal static Pen GetDashDotDotPen(Brush brush, double thickness)
    {
        return GetDashStylePen(brush, thickness, DashDotDotDashArray);
    }
}


[Flags]
internal enum BorderCellFlags
{
    None = 0,

    LeftBound = 0x1,

    TopBound = 0x2,

    RightBound = 0x4,

    BottomBound = 0x8,

    Hidden = 0x10,

    MergedLineInner = 0x20,

    MergedColumnInner = 0x40,

    Merged = MergedLineInner | MergedColumnInner
}